import javax.swing.*;                // 导入Swing GUI组件库（按钮、文本框、面板等）
import javax.swing.border.EmptyBorder; // 导入边框样式类（用于设置组件边距）
import java.awt.*;                   // 导入基础AWT组件和布局管理器
import java.awt.event.ActionEvent;   // 导入事件类（处理用户操作）
import java.awt.event.ActionListener; // 导入事件监听器接口（处理按钮点击）
import java.awt.event.MouseAdapter;  // 导入鼠标事件适配器（简化事件处理）
import java.awt.event.MouseEvent;    // 导入鼠标事件类（获取鼠标操作信息）
import java.io.File;                  // 导入文件操作类（加载资源）
import java.text.SimpleDateFormat;    // 导入日期格式化类（处理时间显示）
import java.util.Date;                // 导入日期类（获取系统时间）
import javax.sound.sampled.*;         // 导入音频播放类（实现按键音效）

/**
 * 高级计算器主类
 * 功能：实现图形界面计算器，支持四则运算、科学计算、日期显示，
 *       包含背景图片、按键音效、输入验证等增强功能
 */
public class Calculator extends JFrame implements ActionListener {
    // 界面组件声明
    private JPanel north, center;         // 顶部面板（显示区）和中部面板（按钮区）
    private JTextField field;             // 计算过程和结果显示文本框
    private JButton[] btns;               // 计算器功能按钮数组
    
    // 多媒体资源声明
    private Clip soundClip;               // 按键音效剪辑对象
    private Image backgroundImage;        // 背景图片对象

    /**
     * 计算器构造方法
     * 初始化窗口基本设置并加载界面组件
     */
    public Calculator() {
        setTitle("高级计算器");           // 设置窗口标题栏显示文本
        setSize(700, 700);                // 设置窗口宽度和高度
        setDefaultCloseOperation(EXIT_ON_CLOSE); // 设置关闭窗口时退出程序
        setLocationRelativeTo(null);      // 设置窗口相对于屏幕居中
        setResizable(false);              // 禁止用户调整窗口大小
        
        // 尝试加载背景图片（处理可能的文件异常）
        try {
            backgroundImage = new ImageIcon("C:\\Users\\DELL\\Desktop\\Test\\OIP-C.jpg").getImage();
        } catch (Exception e) {
            System.out.println("背景图片加载失败，使用默认背景: " + e.getMessage());
        }
        
        initComponent();                  // 调用组件初始化方法
        setVisible(true);                 // 显示窗口（置于初始化之后）
    }

    /**
     * 初始化界面组件
     * 创建面板、文本框、按钮并设置布局样式
     */
    private void initComponent() {
        // 创建自定义背景面板（重写绘制方法以显示背景图片）
        JPanel backgroundPanel = new JPanel(new BorderLayout()) {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (backgroundImage != null) {
                    g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
                }
            }
        };
        setContentPane(backgroundPanel);  // 将自定义面板设为窗口内容面板
        
        // 创建顶部和中部面板（设置为透明以显示背景）
        north = new JPanel();
        center = new JPanel();
        north.setOpaque(false);
        center.setOpaque(false);

        // 初始化显示文本框
        field = new JTextField();
        field.setPreferredSize(new Dimension(700, 150));  // 设置文本框首选尺寸
        field.setEditable(false);                        // 禁止用户直接编辑文本框
        field.setFont(new Font("SansSerif", Font.BOLD, 35)); // 设置字体样式和大小
        field.setHorizontalAlignment(JTextField.RIGHT);  // 文本右对齐显示
        field.setBackground(new Color(240, 240, 240, 180)); // 设置半透明背景色
        field.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 200, 200), 2),
            new EmptyBorder(10, 10, 10, 10)
        )); // 创建复合边框（外框线+内边距）

        // 定义按钮文本（按数字键盘布局排列）
        String[] str = new String[] {
            "7", "8", "9", "+",
            "4", "5", "6", "-",
            "1", "2", "3", "×",
            "0", ".", "=", "÷",
            "C", "B", "T", "S"
        };
        
        // 初始化按钮数组并设置样式
        btns = new JButton[str.length];
        for (int i = 0; i < btns.length; i++) {
            btns[i] = new JButton(str[i]);                 // 创建按钮并设置文本
            btns[i].setFont(new Font("SansSerif", Font.BOLD, 25)); // 设置按钮字体
            btns[i].setFocusPainted(false); // 取消按钮获得焦点时的高亮边框
            btns[i].setOpaque(true);        // 设置按钮为不透明（显示背景色）
            
            // 根据按钮功能设置不同背景色
            if (i % 4 == 3) {              // 操作符按钮（+、-、×、÷）
                btns[i].setBackground(new Color(255, 165, 0, 200));
                btns[i].setForeground(Color.WHITE);
            } else if (str[i].equals("C") || str[i].equals("B")) { // 清除/退格按钮
                btns[i].setBackground(new Color(220, 50, 50, 200));
                btns[i].setForeground(Color.WHITE);
            } else if (str[i].equals("T") || str[i].equals("S")) { // 时间/科学计算按钮
                btns[i].setBackground(new Color(70, 130, 180, 200));
                btns[i].setForeground(Color.WHITE);
            } else {                        // 数字按钮
                btns[i].setBackground(new Color(240, 240, 240, 180));
                btns[i].setForeground(Color.BLACK);
            }
            
            // 添加鼠标事件监听器（实现按钮点击动画效果）
            final JButton currentBtn = btns[i];
            btns[i].addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    currentBtn.setBorder(BorderFactory.createLineBorder(new Color(100, 100, 100), 2));
                    currentBtn.setContentAreaFilled(false); // 点击时按钮透明
                    playButtonSound(); // 播放按键音效
                }
                @Override
                public void mouseReleased(MouseEvent e) {
                    currentBtn.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200), 1));
                    currentBtn.setContentAreaFilled(true);  // 释放后恢复不透明
                }
            });
        }

        // 设置面板布局管理器
        north.setLayout(new BorderLayout());          // 顶部面板使用边界布局
        center.setLayout(new GridLayout(5, 4, 10, 10)); // 中部面板使用5×4网格布局（间距10px）
        center.setBorder(new EmptyBorder(20, 20, 20, 20)); // 设置面板内边距
        
        // 添加组件到面板
        north.add(field, BorderLayout.CENTER);        // 文本框居中添加到顶部面板
        for (JButton btn : btns) {
            center.add(btn);                          // 按钮添加到中部面板
        }
        
        // 添加面板到窗口
        backgroundPanel.add(north, BorderLayout.NORTH);   // 顶部面板置于窗口北部
        backgroundPanel.add(center, BorderLayout.CENTER); // 中部面板置于窗口中部
        
        // 为所有按钮绑定点击事件监听器
        for (JButton btn : btns) {
            btn.addActionListener(this);              // 事件委托给当前类处理
        }
    }

    /**
     * 播放按键音效
     * 从指定路径加载WAV音频文件并播放
     */
    private void playButtonSound() {
        try {
            // 首次播放或音效未打开时加载文件
            if (soundClip == null || !soundClip.isOpen()) {
                File soundFile = new File("C:\\Users\\DELL\\Desktop\\Test\\sample-12s.wav");
                if (soundFile.exists()) {
                    AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
                    soundClip = AudioSystem.getClip();
                    soundClip.open(audioInputStream);
                }
            }
            // 播放音效（重置到开头再播放）
            if (soundClip != null && soundClip.isOpen()) {
                soundClip.setFramePosition(0); // 重置音频播放位置
                soundClip.start(); // 播放音效
            }
        } catch (Exception e) {
            System.out.println("音效播放失败: " + e.getMessage()); // 异常时输出错误信息
        }
    }

    /**
     * 处理按钮点击文本逻辑
     * 根据不同按钮文本执行相应功能
     */
    private void setBtnText(String text) {
        String currentText = field.getText(); // 获取当前显示文本
        switch (text) {
            case "C": // 清除按钮（Clear）
                field.setText(""); // 清空文本框
                break;
                
            case "B": // 退格按钮（Backspace）
                if (!currentText.isEmpty()) {
                    field.setText(currentText.substring(0, currentText.length() - 1)); // 删除最后一个字符
                }
                break;
                
            case "T": // 时间按钮（Time）
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                field.setText(dateFormat.format(new Date())); // 显示当前日期时间
                break;
                
            case "S": // 科学计算按钮（Science）
                String result = "";
                try {
                    double num = Double.parseDouble(currentText); // 将输入转换为数字
                    // 弹出功能选择对话框
                    String[] options = {"正弦", "余弦", "平方根", "对数"};
                    int choice = JOptionPane.showOptionDialog(
                        this, "选择计算功能", "科学计算",
                        JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE,
                        null, options, options[0]
                    );
                    
                    // 根据用户选择执行不同计算
                    switch (choice) {
                        case 0: result = String.format("%.4f", Math.sin(num)); break; // 计算正弦值
                        case 1: result = String.format("%.4f", Math.cos(num)); break; // 计算余弦值
                        case 2: result = String.format("%.4f", Math.sqrt(num)); break; // 计算平方根
                        case 3: result = String.format("%.4f", Math.log(num)); break; // 计算自然对数
                        default: result = currentText; // 用户取消时保留原输入
                    }
                } catch (NumberFormatException e) {
                    result = "请输入数字"; // 非数字输入时提示
                } catch (Exception e) {
                    result = "计算错误"; // 其他异常时提示
                }
                field.setText(result); // 显示计算结果
                break;
                
            case "=": // 等号按钮（Calculate）
                try {
                    // 替换乘除符号为Java可计算的运算符
                    String expression = currentText.replace("×", "*").replace("÷", "/");
                    double resultValue = evaluateExpression(expression); // 计算表达式结果
                    // 格式化结果并去除末尾无效0
                    field.setText(String.format("%.6f", resultValue).replaceAll("\\.?0+$", ""));
                } catch (Exception e) {
                    field.setText("计算错误"); // 计算异常时提示
                }
                break;
                
            case ".": // 小数点按钮（Decimal Point）
                if (currentText.isEmpty()) {
                    field.setText("0."); // 空输入时自动补0（如.5→0.5）
                } else {
                    // 查找最后一个操作符位置（支持×和÷）
                    int lastOpIndex = Math.max(
                        Math.max(currentText.lastIndexOf('+'), currentText.lastIndexOf('-')),
                        Math.max(currentText.lastIndexOf('*'), currentText.lastIndexOf('/'))
                    );
                    int lastOpIndex2 = Math.max(currentText.lastIndexOf('×'), currentText.lastIndexOf('÷'));
                    lastOpIndex = Math.max(lastOpIndex, lastOpIndex2);
                    
                    // 获取当前数字部分（操作符后的内容）
                    String lastPart = (lastOpIndex != -1) ? currentText.substring(lastOpIndex + 1) : currentText;
                    if (!lastPart.contains(".")) {
                        field.setText(currentText + text); // 确保数字部分只有一个小数点
                    }
                }
                break;
                
            default: // 数字或操作符按钮
                if (isOperator(text)) { // 判断是否为操作符
                    // 允许负号作为数字开头（如-5）
                    if (text.equals("-") && currentText.isEmpty()) {
                        field.setText(text);
                    } 
                    // 防止操作符连续出现（如1++2）或出现在开头（非负号情况）
                    else if (!currentText.isEmpty() && !isOperator(currentText.substring(currentText.length() - 1))) {
                        field.setText(currentText + text);
                    }
                } else {
                    // 数字直接添加到文本框
                    field.setText(currentText + text);
                }
                break;
        }
    }

    /**
     * 判断字符是否为操作符
     * @param text 待判断的字符文本
     * @return 是操作符返回true，否则返回false
     */
    private boolean isOperator(String text) {
        return text.equals("+") || text.equals("-") || text.equals("×") || 
               text.equals("÷") || text.equals("*") || text.equals("/");
    }

    /**
     * 计算数学表达式的值
     * 使用递归下降解析器实现表达式解析和计算
     */
    private double evaluateExpression(String expression) {
        // 内部类实现递归下降解析器
        return new Object() {
            int pos = -1, ch; // 当前解析位置和当前字符
            
            // 读取下一个字符
            void nextChar() { ch = (++pos < expression.length()) ? expression.charAt(pos) : -1; }
            
            // 检查当前字符是否为目标字符，若是则前进
            boolean eat(int charToEat) {
                while (ch == ' ') nextChar(); // 跳过空格
                if (ch == charToEat) { nextChar(); return true; }
                return false;
            }
            
            // 解析整个表达式
            double parse() {
                nextChar();
                double x = parseExpression();
                if (pos < expression.length()) throw new RuntimeException("表达式错误");
                return x;
            }
            
            // 解析表达式（处理加减法）
            double parseExpression() {
                double x = parseTerm();
                for (;;) {
                    if (eat('+')) x += parseTerm(); // 处理加法
                    else if (eat('-')) x -= parseTerm(); // 处理减法
                    else return x;
                }
            }
            
            // 解析项（处理乘除法）
            double parseTerm() {
                double x = parseFactor();
                for (;;) {
                    if (eat('*')) x *= parseFactor(); // 处理乘法
                    else if (eat('/')) x /= parseFactor(); // 处理除法
                    else return x;
                }
            }
            
            // 解析因子（处理数字、括号、正负号）
            double parseFactor() {
                if (eat('+')) return parseFactor(); // 处理正号（如+5）
                if (eat('-')) return -parseFactor(); // 处理负号（如-5）
                
                double x; int startPos = this.pos;
                if (eat('(')) {
                    x = parseExpression(); eat(')'); // 递归解析括号内表达式
                } else if (ch == '.') {
                    // 处理以小数点开头的数字（如.5）
                    nextChar();
                    if (ch >= '0' && ch <= '9') {
                        while (ch >= '0' && ch <= '9') nextChar();
                        x = Double.parseDouble(expression.substring(startPos, this.pos));
                    } else {
                        throw new RuntimeException("小数点后必须跟数字");
                    }
                } else if (ch >= '0' && ch <= '9') {
                    // 处理普通数字（如123.45）
                    while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
                    x = Double.parseDouble(expression.substring(startPos, this.pos));
                } else {
                    throw new RuntimeException("非法字符: " + (char)ch);
                }
                return x;
            }
        }.parse();
    }

    // 实现ActionListener接口的事件处理方法
    @Override
    public void actionPerformed(ActionEvent a) {
        if (a.getSource() instanceof JButton) {
            JButton btn = (JButton) a.getSource();
            setBtnText(btn.getText()); // 调用按钮文本处理方法
        }
    }

    /**
     * 程序入口主方法
     * 在事件调度线程中创建计算器实例
     */
    public static void main(String[] args) {
        // 使用SwingUtilities确保在EDT线程中创建GUI（线程安全）
        SwingUtilities.invokeLater(() -> {
            Calculator calculator = new Calculator();
            // 验证资源文件是否存在
            File imgFile = new File("C:\\Users\\DELL\\Desktop\\Test\\OIP-C.jpg");
            File soundFile = new File("C:\\Users\\DELL\\Desktop\\Test\\sample-12s.wav");
            if (!imgFile.exists()) System.out.println("警告：背景图片不存在");
            if (!soundFile.exists()) System.out.println("警告：音效文件不存在");
        });
    }
}